from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel
from typing import List, Optional, Dict
import uvicorn
from companion import VirtualCompanion
from config import settings

from datetime import datetime
import os
import json
import re

app = FastAPI(title="Virtual Companion API")

# 配置CORS - 明确添加前端域名
app.add_middleware(
    CORSMiddleware,
    allow_origins=[
        "http://localhost:5173",  # Vite默认开发服务器
        "http://localhost:3000",  # Express服务器
        "http://127.0.0.1:5173",
        "http://127.0.0.1:3000",
        "*"  # 允许所有源（开发环境）
    ],
    allow_credentials=True,
    allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
    allow_headers=["*"],
    expose_headers=["*"]
)

# 确保图片目录存在
os.makedirs(settings.outfit_images_dir, exist_ok=True)

# 挂载静态文件
app.mount("/images", StaticFiles(directory=settings.outfit_images_dir), name="images")

# 敏感词列表（可以从外部文件加载或数据库）
SENSITIVE_WORDS = [
    # 攻击性词汇
    "傻逼", "傻B", "傻比", "SB", "sb", "煞笔", "傻狗", "贱人", "神经病", "白痴", 
    "废物", "垃圾", "蠢货", "笨蛋", "弱智", "智障", "脑残", "猪脑子", "蠢材",
    
    # 粗俗词汇
    "操你", "日你", "草你", "妈的", "他妈", "尼玛", "滚", "去死", "混蛋", "王八蛋", 
    "狗东西", "婊子", "妓女", "贱货", "骚货", "鸡", "表子",
    
    # 不良情绪词汇
    "烦死你", "恨你", "讨厌你", "厌恶", "恶心", "恶毒", "讨厌死了", "不想理你",
    
    # 其他不良词汇
    "滚蛋", "不爱你", "分手", "绿茶", "渣男", "渣女"
]

# 敏感词分级和替换词映射
SENSITIVITY_LEVELS = {
    "高": [
        "傻逼", "操你", "日你", "草你", "妓女", "贱人", "婊子", "骚货", "尼玛", "他妈"
    ],
    "中": [
        "SB", "sb", "废物", "蠢货", "弱智", "智障", "脑残", "去死", "不爱你", "分手"
    ],
    "低": [
        "笨蛋", "讨厌你", "烦死你", "不想理你", "滚", "滚蛋"
    ]
}

# 敏感词对应的情绪反应
EMOTIONAL_REACTIONS = {
    "高": {
        "reaction": "极度愤怒和伤心",
        "response": "我从来没想到你会说出这么伤人的话，我真的好难过...我需要冷静一下，再见。",
        "mood_change": -50,
        "exit": True
    },
    "中": {
        "reaction": "生气和委屈",
        "response": "你这样说话真的让我很伤心，我们能不能好好说话？不然我真的会生气的...",
        "mood_change": -30,
        "exit": False
    },
    "低": {
        "reaction": "受伤和困惑",
        "response": "你今天怎么了？是不是心情不好？但也不要这样和我说话好吗？",
        "mood_change": -15,
        "exit": False
    }
}

# 数据模型
class Message(BaseModel):
    content: str
    role: str = "user"
    scenario: Optional[str] = None

class Conversation(BaseModel):
    messages: List[Message]

class SensitiveCheckRequest(BaseModel):
    text: str
    user_id: Optional[str] = None

class SensitiveCheckResponse(BaseModel):
    is_sensitive: bool
    matched_words: List[str] = []
    sensitivity_level: Optional[str] = None
    suggested_response: Optional[str] = None
    mood_change: int = 0
    should_exit: bool = False

class CompanionProfile(BaseModel):
    name: str
    personality: str
    interests: List[str]
    background: str
    style_type: Optional[str] = "温柔型"

# 全局虚拟伴侣实例
companion = None

# 路由
@app.get("/")
async def root():
    """API健康检查端点，返回API状态和版本信息"""
    return {
        "status": "ok",
        "message": "Virtual Companion API is running",
        "version": "1.0.0",
        "endpoints": {
            "check-sensitive": "/check-sensitive",
            "chat": "/chat", 
            "companion-profile": "/companion/profile",
            "outfit": "/outfit/today"
        },
        "timestamp": datetime.now().isoformat()
    }

# 敏感词检测接口
@app.post("/check-sensitive", response_model=SensitiveCheckResponse)
async def check_sensitive_words(request: SensitiveCheckRequest):
    """检测文本中是否含有敏感词"""
    text = request.text.lower()
    matched_words = []
    highest_level = None
    
    # 检查所有敏感词
    for word in SENSITIVE_WORDS:
        pattern = r'\b{}\b'.format(re.escape(word)) if len(word) > 1 and word.isalpha() else word
        if re.search(pattern, text, re.IGNORECASE):
            matched_words.append(word)
    
    # 无敏感词
    if not matched_words:
        return SensitiveCheckResponse(
            is_sensitive=False,
            matched_words=[],
            sensitivity_level=None
        )
    
    # 确定最高敏感级别
    for level in ["高", "中", "低"]:
        if any(word in SENSITIVITY_LEVELS[level] for word in matched_words):
            highest_level = level
            break
    
    if not highest_level:
        highest_level = "低"  # 默认级别
    
    # 获取对应的情绪反应
    reaction = EMOTIONAL_REACTIONS[highest_level]
    
    # 如果是高级别敏感词，可以记录用户行为（可扩展功能）
    if highest_level == "高" and request.user_id:
        # 这里可以记录用户不良行为到数据库
        pass
        
    return SensitiveCheckResponse(
        is_sensitive=True,
        matched_words=matched_words,
        sensitivity_level=highest_level,
        suggested_response=reaction["response"],
        mood_change=reaction["mood_change"],
        should_exit=reaction["exit"]
    )

@app.post("/companion/profile")
async def create_profile(profile: CompanionProfile):
    global companion
    companion = VirtualCompanion(
        name=profile.name,
        personality=profile.personality,
        interests=profile.interests,
        background=profile.background,
        style_type=profile.style_type  # 新增
    )
    return {"message": "Profile created successfully", "profile": companion.get_profile()}

@app.get("/companion/profile")
async def get_profile():
    if not companion:
        raise HTTPException(status_code=404, detail="Companion profile not created")
    return companion.get_profile()

def extract_text_field(text):
    import json
    for _ in range(3):  # 最多递归3层
        try:
            parsed = json.loads(text)
            if isinstance(parsed, dict):
                # 优先找 text，其次 answer
                if "text" in parsed:
                    text = parsed["text"]
                elif "answer" in parsed:
                    text = parsed["answer"]
                else:
                    break
            else:
                break
        except Exception:
            break
    return text

@app.post("/chat")
async def chat(conversation: Conversation):
    if not companion:
        raise HTTPException(status_code=404, detail="Companion profile not created")
    if not conversation.messages:
        raise HTTPException(status_code=400, detail="No messages provided")
    # 用前端传来的所有历史，重建 conversation_history
    from datetime import datetime
    companion.conversation_history = [
        {
            "role": msg.role if msg.role != "companion" else "assistant",
            "content": msg.content,
            "timestamp": datetime.now().isoformat(),
            "scenario": getattr(msg, "scenario", None) or getattr(companion, "current_scenario", "casual")
        }
        for msg in conversation.messages
    ]
    last_message = conversation.messages[-1]
    result = companion.process_message(
        last_message.content,
        scenario=last_message.scenario
    )
    # 只返回字符串
    if isinstance(result, dict) and "text" in result:
        text = result["text"]
    elif isinstance(result, str):
        text = result
    else:
        text = str(result)
    # 如果 text 是 JSON 字符串，尝试只取 text 字段
    try:
        import json
        parsed = json.loads(text)
        if isinstance(parsed, dict) and "text" in parsed:
            text = parsed["text"]
    except Exception:
        pass
    text = extract_text_field(text)
    return {
        "text": text,
        "emotional_state": getattr(companion, "emotional_state", None),
        "behavior_patterns": getattr(companion, "behavior_patterns", None),
        "current_scenario": getattr(companion, "current_scenario", None)
    }

@app.get("/scenarios")
async def get_scenarios():
    return settings.CONVERSATION_SCENARIOS

@app.post("/scenario/{scenario_name}")
async def set_scenario(scenario_name: str):
    if not companion:
        raise HTTPException(status_code=404, detail="Companion profile not created")
    
    if companion.set_scenario(scenario_name):
        return {"message": f"Scenario set to {scenario_name}"}
    else:
        raise HTTPException(status_code=400, detail="Invalid scenario")
 
@app.get("/conversation/history")
async def get_conversation_history():
    if not companion:
        raise HTTPException(status_code=404, detail="Companion profile not created")
    return {"history": companion.conversation_history}

@app.post("/conversation/save")
async def save_conversation(filename: str = "conversation_history.json"):
    if not companion:
        raise HTTPException(status_code=404, detail="Companion profile not created")
    companion.save_conversation_history(filename)
    return {"message": f"Conversation history saved to {filename}"}

@app.post("/conversation/load")
async def load_conversation(filename: str = "conversation_history.json"):
    if not companion:
        raise HTTPException(status_code=404, detail="Companion profile not created")
    companion.load_conversation_history(filename)
    return {"message": f"Conversation history loaded from {filename}"}

# 新增：扩展敏感词库接口
@app.post("/sensitive-words/add")
async def add_sensitive_words(words: List[str], level: Optional[str] = "低"):
    """添加新的敏感词到指定级别"""
    global SENSITIVE_WORDS
    
    if level not in ["高", "中", "低"]:
        raise HTTPException(status_code=400, detail="Invalid sensitivity level")
    
    for word in words:
        if word not in SENSITIVE_WORDS:
            SENSITIVE_WORDS.append(word)
            
        # 同时添加到分级列表
        if word not in SENSITIVITY_LEVELS[level]:
            SENSITIVITY_LEVELS[level].append(word)
            
    return {"message": f"Added {len(words)} words to sensitivity level '{level}'"}

# 新增：今日穿搭相关路由
@app.get("/outfit/today")
async def today_outfit(force: bool = False, style_type: Optional[str] = None):
    """获取今日穿搭推荐和图片"""
    try:
        # 调用穿搭生成器
        outfit = get_today_outfit(force=force, style_type=style_type)
        
        # 确保返回数据包含必要字段，防止503错误
        if not outfit:
            # 返回默认穿搭数据
            from datetime import datetime
            default_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "outfit_images", "default_outfit.png")
            today = datetime.now().strftime("%Y-%m-%d")
            outfit = {
                "date": today,
                "style": style_type or "休闲风格",
                "clothes": "舒适时尚服装",
                "weather": "天气宜人",
                "prompt": "时尚服装搭配",
                "image_path": default_path
            }
        
        # 确保返回正确格式的数据
        return create_outfit_response(outfit)
    except Exception as e:
        # 记录错误，但返回默认数据而不是错误
        import logging
        logging.error(f"获取穿搭失败: {str(e)}")
        
        # 返回默认穿搭数据
        from datetime import datetime
        default_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "outfit_images", "default_outfit.png")
        today = datetime.now().strftime("%Y-%m-%d")
        
        # 创建默认穿搭数据并格式化
        default_outfit = {
            "date": today,
            "style": style_type or "休闲风格",
            "clothes": "舒适时尚服装",
            "weather": "天气宜人",
            "image_path": default_path
        }
        
        # 使用create_outfit_response格式化返回数据
        return create_outfit_response(default_outfit)

# 强制生成新的穿搭
@app.post("/outfit/generate")
async def generate_outfit(style_type: Optional[str] = None):
    """强制生成新的穿搭"""
    try:
        # 调用穿搭生成器，强制生成新的
        outfit = get_today_outfit(force=True, style_type=style_type)
        
        # 确保返回数据包含必要字段，防止503错误
        if not outfit:
            # 返回默认穿搭数据
            from datetime import datetime
            default_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "outfit_images", "default_outfit.png")
            today = datetime.now().strftime("%Y-%m-%d")
            outfit = {
                "date": today,
                "style": style_type or "休闲风格",
                "clothes": "舒适时尚服装",
                "weather": "天气宜人",
                "prompt": "时尚服装搭配",
                "image_path": default_path
            }
        
        # 确保返回正确格式的数据
        return create_outfit_response(outfit)
    except Exception as e:
        # 记录错误，但返回默认数据而不是错误
        import logging
        import traceback
        logging.error(f"生成穿搭失败: {str(e)}")
        logging.error(traceback.format_exc())
        
        # 返回默认穿搭数据
        from datetime import datetime
        default_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "outfit_images", "default_outfit.png")
        today = datetime.now().strftime("%Y-%m-%d")
        
        # 创建默认穿搭数据并格式化
        default_outfit = {
            "date": today,
            "style": style_type or "休闲风格",
            "clothes": "舒适时尚服装",
            "weather": "天气宜人",
            "image_path": default_path
        }
        
        # 使用create_outfit_response格式化返回数据
        return create_outfit_response(default_outfit)

if __name__ == "__main__":
    uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)